
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
	<title>YUI Library Examples: Widget: Creating a simple Tooltip widget</title>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    	<link rel="stylesheet" type="text/css" href="../../assets/yui.css" >

<style>
    /*Supplemental CSS for the YUI distribution*/
    #custom-doc { width: 95%; min-width: 950px; }
    #pagetitle {background-image: url(../../assets/bg_hd.gif);}
/*    #pagetitle h1 {background-image: url(../../assets/title_h_bg.gif);}*/
</style>

<link rel="stylesheet" type="text/css" href="../../assets/dpSyntaxHighlighter.css">
<link type="text/css" rel="stylesheet" href="../../build/cssfonts/fonts-min.css" />
<script type="text/javascript" src="../../build/yui/yui-min.js"></script>
<style type="text/css">
    .yui-tooltip {
        position:absolute;
    }

    .yui-tooltip-content {
        color: #000;
        padding: 2px 5px;
        border-color: #D4C237 #A6982B #A6982B #A6982B;
        border-width: 1px;
        border-style: solid;
        background-color: #FFEE69;
    }

    .yui-tooltip-hidden {
        visibility:hidden;
    }

    div.yui-hastooltip {
        border:1px solid #243356;
        background-color:#406ED9;
        color:#ffffff;
        width:25em;
        margin:20px 0px;
        padding:5px;
        cursor:default;
    }

    div.yui-hastooltip span {
        font-style:italic;
        font-weight:bold;
        color:#ABCEFF;
    }

    .yui-tooltip-content strong {
        font-weight:bold;
    }
</style>

</head>
<body id="yahoo-com" class=" yui-skin-sam">
<div id="custom-doc" class="yui-t2">
<div id="hd">
	<div id="ygunav">
		<p>
            <em>
                <a href="http://developer.yahoo.com/yui/3/">YUI 3.x Home</a> <i> - </i>	
            </em>
		</p>
		<form action="http://search.yahoo.com/search" id="sitesearchform">
            <input name="vs" type="hidden" value="developer.yahoo.com">
            <input name="vs" type="hidden" value="yuiblog.com">
		    <div id="sitesearch">
		    	<label for="searchinput">Site Search (YDN &amp; YUIBlog): </label>
			    <input type="text" id="searchinput" name="p">
			    <input type="submit" value="Search" id="searchsubmit" class="ygbt">
		    </div>
		</form>
    </div>
	<div id="ygma"><a href="../../"><img src="../../assets/logo.gif"  border="0" width="200" height="93"></a></div>
	<div id="pagetitle"><h1>YUI Library Examples: Widget: Creating a simple Tooltip widget</h1></div>
</div>
<div id="bd">

	<div id="bar-note"><p><strong>Note:</strong> This is YUI 3.x. Looking for <a href="http://developer.yahoo.com/yui/">YUI 2.x</a>?</p></div>

	<div id="yui-main">
		<div class="yui-b">
		  <div class="yui-ge">
			  <div class="yui-u first example" id="main">

	<h2>Widget: Creating a simple Tooltip widget</h2>

	<div id="example" class="promo">
	<p>
	This is an advanced example, in which we create a Tooltip widget, by extending the base <code>Widget</code> class, and adding <code>WidgetStack</code> and <code>WidgetPosition</code> extensions, through <code>Base.build</code>.	</p>	

	<div class="module example-container ">
			<div class="hd exampleHd">
			<p class="newWindowButton yui-skin-sam">
                <a href="widget-tooltip_clean.html" target="_blank">View example in new window.</a>
            </p>
		</div>		<div id="example-canvas" class="bd">

		
	<!--BEGIN SOURCE CODE FOR EXAMPLE =============================== -->
	
	<div id="delegate">
    <div class="yui-hastooltip" title="Tooltip 1" id="tt1">Tooltip One <span>(content from title)</span></div>
    <div class="yui-hastooltip" title="Tooltip 2" id="tt2">Tooltip Two <span>(content set in event listener)</span></div>
    <div class="yui-hastooltip" title="Tooltip 3" id="tt3">Tooltip Three <span>(content from lookup)</span></div>
    <div class="yui-hastooltip" title="Tooltip 4" id="tt4">Tooltip Four <span>(content from title)</span><label></div>
    <input type="checkbox" id="prevent" /> Prevent Tooltip Four</label>
</div>

<script type="text/javascript">
YUI({base:"../../build/", timeout: 10000}).use("widget", "widget-position", "widget-stack", function(Y) {
    var Lang = Y.Lang,
        Node = Y.Node;

    /* Tooltip constructor */
    function Tooltip(config) {
        Tooltip.superclass.constructor.apply(this, arguments);
    }

    /* 
       Required NAME static field, used to identify the Widget class and 
       used as an event prefix, to generate class names etc. (set to the 
       class name in camel case). 
    */
    Tooltip.NAME = "tooltip";

    /* Static constants */
    Tooltip.OFFSET_X = 15;
    Tooltip.OFFSET_Y = 15;
    Tooltip.OFFSCREEN_X = -10000;
    Tooltip.OFFSCREEN_Y = -10000;

    /* Default Tooltip Attributes */
    Tooltip.ATTRS = {

        /* 
         * The tooltip content. This can either be a fixed content value, 
         * or a map of id-to-values, designed to be used when a single
         * tooltip is mapped to multiple trigger elements.
         */
        content : {
            value: null
        },

        /* 
         * The set of nodes to bind to the tooltip instance. Can be a string, 
         * or a node instance.
         */
        triggerNodes : {
            value: null,
            setter: function(val) {
                if (val && Lang.isString(val)) {
                    val = Node.all(val);
                }
                return val;
            }
        },

        /*
         * The delegate node to which event listeners should be attached.
         * This node should be an ancestor of all trigger nodes bound
         * to the instance. By default the document is used.
         */
        delegate : {
            value: null,
            setter: function(val) {
                return Node.get(val) || Node.get("document");
            }
        },

        /*
         * The time to wait, after the mouse enters the trigger node,
         * to display the tooltip
         */
        showDelay : {
            value:250
        },

        /*
         * The time to wait, after the mouse leaves the trigger node,
         * to hide the tooltip
         */
        hideDelay : {
            value:10
        },

        /*
         * The time to wait, after the tooltip is first displayed for 
         * a trigger node, to hide it, if the mouse has not left the 
         * trigger node
         */
        autoHideDelay : {
            value:2000
        },

        /*
         * Override the default visibility set by the widget base class
         */
        visible : {
            value:false
        },

        /*
         * Override the default XY value set by the widget base class,
         * to position the tooltip offscreen
         */
        xy: {
            value:[Tooltip.OFFSCREEN_X, Tooltip.OFFSCREEN_Y]
        }
    };

    /* Extend the base Widget class */
    Y.extend(Tooltip, Y.Widget, {

        /*
         * Initialization Code: Sets up privately used state
         * properties, and publishes the events Tooltip introduces
         */
        initializer : function(config) {

            this._triggerClassName = this.getClassName("trigger");

            // Currently bound trigger node information
            this._currTrigger = {
                node: null,
                title: null,
                mouseX: Tooltip.OFFSCREEN_X,
                mouseY: Tooltip.OFFSCREEN_Y
            };

            // Event handles - mouse over is set on the delegate
            // element, mousemove and mouseout are set on the trigger node
            this._eventHandles = {
                delegate: null,
                trigger: {
                    mouseMove : null,
                    mouseOut: null
                }
            };

            // Show/hide timers
            this._timers = {
                show: null,
                hide: null
            };

            // Publish events introduced by Tooltip. Note the triggerEnter event is preventable,
            // with the default behavior defined in the _defTriggerEnterFn method 
            this.publish("triggerEnter", {defaultFn: this._defTriggerEnterFn, preventable:true});
            this.publish("triggerLeave", {preventable:false});
        },

        /*
         * Destruction Code: Clears event handles, timers,
         * and current trigger information
         */
        destructor : function() {
            this._clearCurrentTrigger();
            this._clearTimers();
            this._clearHandles();
        },

        /*
         * bindUI is used to bind attribute change and dom event
         * listeners
         */
        bindUI : function() {
            this.after("delegateChange", this._afterSetDelegate);
            this.after("nodesChange", this._afterSetNodes);

            this._bindDelegate();
        },

        /*
         * syncUI is used to update the rendered DOM, based on the current
         * Tooltip state
         */
        syncUI : function() {
            this._uiSetNodes(this.get("triggerNodes"));
        },

        /*
         * Public method, which can be used by triggerEvent event listeners
         * to set the content of the tooltip for the current trigger node
         */
        setTriggerContent : function(content) {
            var contentBox = this.get("contentBox");
            contentBox.set("innerHTML", "");

            if (content) {
                if (content instanceof Node) {
                    for (var i = 0, l = content.size(); i < l; ++i) {
                        contentBox.appendChild(content.item(i));
                    }
                } else if (Lang.isString(content)) {
                    contentBox.set("innerHTML", content);
                }
            }
        },

        /*
         * Gets the closest ancestor of the given node,
         * which is a tooltip trigger node
         */
        getParentTrigger : function(node) {
            var cn = this._triggerClassName;
            return (node.hasClass(cn)) ? node : node.ancestor(function(node) {node.hasClass(cn)});
        },

        /*
         * Default attribute change listener for 
         * the triggerNodes attribute
         */
        _afterSetNodes : function(e) {
            this._uiSetNodes(e.newVal);
        },

        /*
         * Default attribute change listener for 
         * the delegate attribute
         */
        _afterSetDelegate : function(e) {
            this._bindDelegate(e.newVal);
        },

        /*
         * Updates the rendered DOM to reflect the
         * set of trigger nodes passed in
         */
        _uiSetNodes : function(nodes) {
            if (this._triggerNodes) {
                this._triggerNodes.removeClass(this._triggerClassName);
            }

            if (nodes) {
                this._triggerNodes = nodes;
                this._triggerNodes.addClass(this._triggerClassName);
            }
        },

        /*
         * Attaches the default mouseover DOM listener to the 
         * current delegate node
         */
        _bindDelegate : function() {
            var eventHandles = this._eventHandles;

            if (eventHandles.delegate) {
                eventHandles.delegate.detach();
                eventHandles.delegate = null;
            }
            eventHandles.delegate = Y.on("mouseover", Y.bind(this._onDelegateMouseOver, this), this.get("delegate"));
        },

        /*
         * Default mouse over DOM event listener.
         * 
         * Delegates to the _enterTrigger method,
         * if the mouseover enters a trigger node.
         */
        _onDelegateMouseOver : function(e) {
            var node = this.getParentTrigger(e.target);
            if (node && (!this._currTrigger.node || !node.compareTo(this._currTrigger.node))) {
                this._enterTrigger(node, e.pageX, e.pageY);
            }
        },

        /*
         * Default mouse out DOM event listener
         * 
         * Delegates to _leaveTrigger if the mouseout
         * leaves the current trigger node
         */
        _onNodeMouseOut : function(e) {
            var to = e.relatedTarget;
            var trigger = e.currentTarget;

            if (!trigger.contains(to)) {
                this._leaveTrigger(trigger);
            }
        },

        /*
         * Default mouse move DOM event listener
         */
        _onNodeMouseMove : function(e) {
            this._overTrigger(e.pageX, e.pageY);
        },

        /*
         * Default handler invoked when the mouse enters
         * a trigger node. Fires the triggerEnter
         * event which can be prevented by listeners to 
         * show the tooltip from being displayed.
         */
        _enterTrigger : function(node, x, y) {
            this._setCurrentTrigger(node, x, y);
            this.fire("triggerEnter", {node:node, pageX:x, pageY:y});
        },

        /*
         * Default handler for the triggerEvent event,
         * which will setup the timer to display the tooltip,
         * if the default handler has not been prevented.
         */
        _defTriggerEnterFn : function(e) {
            var node = e.node;
            if (!this.get("disabled")) {
                this._clearTimers();
                var delay = (this.get("visible")) ? 0 : this.get("showDelay");
                this._timers.show = Y.later(delay, this, this._showTooltip, [node]);
            }
        },

        /*
         * Default handler invoked when the mouse leaves
         * the current trigger node. Fires the triggerLeave
         * event and sets up the hide timer
         */
        _leaveTrigger : function(node) {
            this.fire("triggerLeave");

            this._clearCurrentTrigger();
            this._clearTimers();

            this._timers.hide = Y.later(this.get("hideDelay"), this, this._hideTooltip);
        },

        /*
         * Default handler invoked for mousemove events
         * on the trigger node. Stores the current mouse 
         * x, y positions
         */
        _overTrigger : function(x, y) {
            this._currTrigger.mouseX = x;
            this._currTrigger.mouseY = y;
        },

        /*
         * Shows the tooltip, after moving it to the current mouse
         * position.
         */
        _showTooltip : function(node) {
            var x = this._currTrigger.mouseX;
            var y = this._currTrigger.mouseY;

            this.move(x + Tooltip.OFFSET_X, y + Tooltip.OFFSET_Y);

            this.show();
            this._clearTimers();

            this._timers.hide = Y.later(this.get("autoHideDelay"), this, this._hideTooltip);
        },

        /*
         * Hides the tooltip, after clearing existing timers.
         */
        _hideTooltip : function() {
            this._clearTimers();
            this.hide();
        },

        /*
         * Set the rendered content of the tooltip for the current
         * trigger, based on (in order of precedence):
         * 
         * a). The string/node content attribute value
         * b). From the content lookup map if it is set, or 
         * c). From the title attribute if set.
         */
        _setTriggerContent : function(node) {
            var content = this.get("content");
            if (content && !(content instanceof Node || Lang.isString(content))) {
                content = content[node.get("id")] || node.getAttribute("title");
            }
            this.setTriggerContent(content);
        },

        /*
         * Set the currently bound trigger node information, clearing 
         * out the title attribute if set and setting up mousemove/out 
         * listeners.
         */
        _setCurrentTrigger : function(node, x, y) {

            var currTrigger = this._currTrigger,
                triggerHandles = this._eventHandles.trigger;

            this._setTriggerContent(node);

            triggerHandles.mouseMove = Y.on("mousemove", Y.bind(this._onNodeMouseMove, this), node);
            triggerHandles.mouseOut = Y.on("mouseout", Y.bind(this._onNodeMouseOut, this), node);

            var title = node.getAttribute("title");
            node.setAttribute("title", "");

            currTrigger.mouseX = x;
            currTrigger.mouseY = y;
            currTrigger.node = node;
            currTrigger.title = title;
        },

        /*
         * Clear out the current trigger state, restoring
         * the title attribute on the trigger node, 
         * if it was originally set.
         */
        _clearCurrentTrigger : function() {

            var currTrigger = this._currTrigger,
                triggerHandles = this._eventHandles.trigger;

            if (currTrigger.node) {
                var node = currTrigger.node;
                var title = currTrigger.title || "";

                currTrigger.node = null;
                currTrigger.title = "";

                triggerHandles.mouseMove.detach();
                triggerHandles.mouseOut.detach();
                triggerHandles.mouseMove = null;
                triggerHandles.mouseOut = null;

                node.setAttribute("title", title);
            }
        },

        /*
         * Cancel any existing show/hide timers
         */
        _clearTimers : function() {
            var timers = this._timers;
            if (timers.hide) {
                timers.hide.cancel();
                timers.hide = null;
            }
            if (timers.show) {
              timers.show.cancel();
              timers.show = null;
            }
        },

        /*
         * Detach any stored event handles
         */
        _clearHandles : function() {
            var eventHandles = this._eventHandles;

            if (eventHandles.delegate) {
                this._eventHandles.delegate.detach();
            }
            if (eventHandles.trigger.mouseOut) {
                eventHandles.trigger.mouseOut.detach();
            }
            if (eventHandles.trigger.mouseMove) {
                eventHandles.trigger.mouseMove.detach();
            }
        }
    });

    // dynamic:false = Modify the existing Tooltip class
    Tooltip = Y.Base.build(Tooltip.NAME, Tooltip, [Y.WidgetPosition, Y.WidgetStack], {dynamic:false});

    var tt = new Tooltip({
        triggerNodes:".yui-hastooltip",
        delegate: "#delegate",
        content: {
            tt3: "Tooltip 3 (from lookup)"
        },
        shim:false,
        zIndex:2
    });
    tt.render();

    tt.on("triggerEnter", function(e) {
        var node = e.node;
        if (node && node.get("id") == "tt2") {
            this.setTriggerContent("Tooltip 2 (from triggerEvent)");
        }
    });

    var prevent = Y.Node.get("#prevent");
    tt.on("triggerEnter", function(e) {
        var node = e.node;
        if (prevent.get("checked")) {
            if (node && node.get("id") == "tt4") {
                e.preventDefault();
            }
        }
    });
});
</script>
	
	<!--END SOURCE CODE FOR EXAMPLE =============================== -->
	
		
		</div>
	</div>			
	</div>
		
	<h3>Creating A Tooltip Widget Class</h3>

<h4>Basic Class Structure</h4>

<p>As with the basic <a href="widget-extend.html">"Extending Widget"</a> example, the <code>Tooltip</code> class will extend the <code>Widget</code> base class and follows the same pattern we use for other classes which extend Base.</p>

<p>Namely:</p>

<ul>
    <li>Set up the constructor to invoke the superclass constructor</li>
    <li>Define a <code>NAME</code> property, to identify the class</li>
    <li>Define the default attribute configuration, using the <code>ATTRS</code> property</li>
    <li>Implement prototype methods</li>
</ul>

<p>This basic structure is shown below:</p>

<textarea name="code" class="JScript" rows="1" cols="60">
    /* 
     *  Required NAME static field, used to identify the Widget class and 
     *  used as an event prefix, to generate class names etc. (set to the 
     *  class name in camel case). 
     */
    Tooltip.NAME = "tooltip";

    /* Default Tooltip Attributes */
    Tooltip.ATTRS = {

        /* 
         * The tooltip content. This can either be a fixed content value, 
         * or a map of id-to-values, designed to be used when a single
         * tooltip is mapped to multiple trigger elements.
         */
        content : {
            value: null
        },

        /* 
         * The set of nodes to bind to the tooltip instance. Can be a string, 
         * or a node instance.
         */
        triggerNodes : {
            value: null,
            setter: function(val) {
                if (val && Lang.isString(val)) {
                    val = Node.all(val);
                }
                return val;
            }
        },

        /*
         * The delegate node to which event listeners should be attached.
         * This node should be an ancestor of all trigger nodes bound
         * to the instance. By default the document is used.
         */
        delegate : {
            value: null,
            setter: function(val) {
                return Node.get(val) || Node.get("document");
            }
        },

        /*
         * The time to wait, after the mouse enters the trigger node,
         * to display the tooltip
         */
        showDelay : {
            value:250
        },

        /*
         * The time to wait, after the mouse leaves the trigger node,
         * to hide the tooltip
         */
        hideDelay : {
            value:10
        },

        /*
         * The time to wait, after the tooltip is first displayed for 
         * a trigger node, to hide it, if the mouse has not left the 
         * trigger node
         */
        autoHideDelay : {
            value:2000
        },

        /*
         * Override the default visibility set by the widget base class
         */
        visible : {
            value:false
        },

        /*
         * Override the default XY value set by the widget base class,
         * to position the tooltip offscreen
         */
        xy: {
            value:[Tooltip.OFFSCREEN_X, Tooltip.OFFSCREEN_Y]
        }
    };

    Y.extend(Tooltip, Y.Widget, { 
        // Prototype methods/properties
    });
</textarea>

<h4>Adding WidgetPosition and WidgetStack Extension Support</h4>

<p>The Tooltip class also needs basic positioning and stacking (z-index, shimming) support. As with the <a href="widget-build.html">Custom Widget Classes</a> example, we use
<code>Base.build</code> to add this support to the <code>Tooltip</code> class. However in this case, we want to modify the Tooltip class, as opposed to extending it 
to create a completely new class, hence we set the <code>dynamic</code> configuration property to <code>false</code>:</p>

<textarea name="code" class="JScript" rows="1" cols="60">
    // dynamic:false = Modify the existing Tooltip class
    Tooltip = Y.Base.build("tooltip", Tooltip, [Y.WidgetPosition, Y.WidgetStack], {dynamic:false});
</textarea>

<h4>Lifecycle Methods: initializer, destructor</h4>

<p>The <code>initializer</code> method is invoked during the <code>init</code> lifecycle phase, after the attributes are configured for each class. <code>Tooltip</code> uses it 
to setup the private state variables it will use to store the trigger node currently being serviced by the tooltip instance, event handles and show/hide timers.</p>

<textarea name="code" class="JScript" rows="1" cols="60">

    initializer : function(config) {

        this._triggerClassName = this.getClassName("trigger");

        // Currently bound trigger node information
        this._currTrigger = {
            node: null,
            title: null,
            mouseX: Tooltip.OFFSCREEN_X,
            mouseY: Tooltip.OFFSCREEN_Y
        };

        // Event handles - mouse over is set on the delegate
        // element, mousemove and mouseout are set on the trigger node
        this._eventHandles = {
            delegate: null,
            trigger: {
                mouseMove : null,
                mouseOut: null
            }
        };

        // Show/hide timers
        this._timers = {
            show: null,
            hide: null
        };

        // Publish events introduced by Tooltip. Note the triggerEnter event is preventable,
        // with the default behavior defined in the _defTriggerEnterFn method 
        this.publish("triggerEnter", {defaultFn: this._defTriggerEnterFn, preventable:true});
        this.publish("triggerLeave", {preventable:false});
    }
</textarea>

<p>The <code>destructor</code> is used to clear out stored state, detach any event handles and clear out the show/hide timers:</p>

<textarea name="code" class="JScript" rows="1" cols="60">
    destructor : function() {
        this._clearCurrentTrigger();
        this._clearTimers();
        this._clearHandles();
    }
</textarea>

<h4>Lifecycle Methods: bindUI, syncUI</h4>

<p>The <code>bindUI</code> and <code>syncUI</code> are invoked by the base Widget class' <code>renderer</code> method.</p>

<p><code>bindUI</code> is used to bind the attribute change listeners used to update the rendered UI from the current state of the widget and also to bind
the DOM listeners required to enable the UI for interaction.</p>

<p><code>syncUI</code> is used to sync the UI state from the current widget state, when initially rendered.</p>

<p><em>NOTE:</em> Widget's <code>renderer</code> method also invokes the <code>renderUI</code> method, which is responsible for laying down any additional content elements a widget requires. However
tooltip does not have any additional elements in needs to add to the DOM, outside of the default Widget boundingBox and contentBox.</p>

<textarea name="code" class="JScript" rows="1" cols="60">
    bindUI : function() {
        this.after("delegateChange", this._afterSetDelegate);
        this.after("nodesChange", this._afterSetNodes);

        this._bindDelegate();
    },

    syncUI : function() {
        this._uiSetNodes(this.get("triggerNodes"));
    }
</textarea>

<h4>Attribute Supporting Methods</h4>

<p>Tooltip's <code>triggerNodes</code>, which defines the set of nodes which should trigger this tooltip instance,
has a couple of supporting methods associated with it.</p>

<p>The <code>_afterSetNodes</code> method is the default attribute change event handler for the <code>triggerNodes</code>
attribute. It invokes the <code>_uiSetNodes</code> method, which marks all trigger nodes with a trigger class name (<code>yui-tooltip-trigger</code>) when set.</p>

<textarea name="code" class="JScript" rows="1" cols="60">
    _afterSetNodes : function(e) {
        this._uiSetNodes(e.newVal);
    },

    _uiSetNodes : function(nodes) {
        if (this._triggerNodes) {
            this._triggerNodes.removeClass(this._triggerClassName);
        }

        if (nodes) {
            this._triggerNodes = nodes;
            this._triggerNodes.addClass(this._triggerClassName);
        }
    },
</textarea>

<p>Similarly the <code>_afterSetDelegate</code> method is the default attributechange listener for the <code>delegate</code> attribute,
and invokes <code>_bindDelegate</code> to set up the listeners when a new delegate node is set.</p>

<textarea name="code" class="JScript" rows="1" cols="60">
    _afterSetDelegate : function(e) {
        this._bindDelegate(e.newVal);
    },

    _bindDelegate : function() {
        var eventHandles = this._eventHandles;

        if (eventHandles.delegate) {
            eventHandles.delegate.detach();
            eventHandles.delegate = null;
        }
        eventHandles.delegate = Y.on("mouseover", Y.bind(this._onDelegateMouseOver, this), this.get("delegate"));
    },
</textarea>

<h4>DOM Event Handlers</h4>

<p>Tooltips interaction revolves around the <code>mouseover</code>, <code>mousemove</code> and <code>mouseout</code> DOM events. The mouseover listener is the only listener set up initially, on the <code>delegate</code> node:</p>

<textarea name="code" class="JScript" rows="1" cols="60">
    _onDelegateMouseOver : function(e) {
        var node = this.getParentTrigger(e.target);
        if (node && (!this._currTrigger.node || !node.compareTo(this._currTrigger.node))) {
            this._enterTrigger(node, e.pageX, e.pageY);
        }
    }
</textarea>

<p>It attempts to determine if the mouse is entering a trigger node. It ignores mouseover events generated from elements 
inside the current trigger node (for example when mousing out of a child element of a trigger node). If it determines that the mouse is entering a trigger node,
the delegates to the <code>_enterTrigger</code> method to setup the current trigger state and attaches mousemove and mouseout listeners on the current trigger node.</p>

<p>The mouse out listener delegates to the <code>_leaveTrigger</code> method, if it determines the mouse is leaving the trigger node:</p>

<textarea name="code" class="JScript" rows="1" cols="60">
    _onNodeMouseOut : function(e) {
        var to = e.relatedTarget;
        var trigger = e.currentTarget;

        if (!trigger.contains(to)) {
            this._leaveTrigger(trigger);
        }
    }
</textarea>

<p>The mouse move listener delegates to the <code>_overTrigger</code> method to store the current mouse XY co-ordinates (used to position the Tooltip when it is displayed after the <code>showDelay</code>):</p>

<textarea name="code" class="JScript" rows="1" cols="60">
    _onNodeMouseMove : function(e) {
        this._overTrigger(e.pageX, e.pageY);
    }
</textarea>

<h4>Trigger Event Delegates: _enterTrigger, _leaveTrigger, _overTrigger</h4>

<p>As seen above, the DOM event handlers delegate to the <code>_enterTrigger, _leaveTrigger and _overTrigger</code> methods to update the 
Tooltip state based on the currently active trigger node.</p>

<p>The <code>_enterTrigger</code> method sets the current trigger state (which node is the current tooltip trigger, 
what the current mouse XY position is, etc.). The method also fires the <code>triggerEnter</code> event, whose default function actually handles 
showing the tooltip after the configured <code>showDelay</code> period. The <code>triggerEnter</code> event can be prevented by listeners, allowing 
users to prevent the tooltip from being shown if required. (<code>triggerEnter</code> listeners are passed the current trigger node and pageX, pageY mouse co-ordinates as event facade properties):</p>

<textarea name="code" class="JScript" rows="1" cols="60">
    _enterTrigger : function(node, x, y) {
        this._setCurrentTrigger(node, x, y);
        this.fire("triggerEnter", null, node, x, y);
    },

    _defTriggerEnterFn : function(e) {
        var node = e.node;
        if (!this.get("disabled")) {
            this._clearTimers();
            var delay = (this.get("visible")) ? 0 : this.get("showDelay");
            this._timers.show = Y.later(delay, this, this._showTooltip, [node]);
        }
    },
</textarea>

<p>Similarly the <code>_leaveTrigger</code> method is invoked when the mouse leaves a trigger node, and clears any stored state, timers and listeners before setting up
the <code>hideDelay</code> timer. It fires a <code>triggerLeave</code> event, but cannot be prevented, and has no default behavior to prevent:</p>

<textarea name="code" class="JScript" rows="1" cols="60">
    _leaveTrigger : function(node) {
        this.fire("triggerLeave");

        this._clearCurrentTrigger();
        this._clearTimers();

        this._timers.hide = Y.later(this.get("hideDelay"), this, this._hideTooltip);
    },
</textarea>

<p>As mentioned previously, the <code>_overTrigger</code> method simply stores the current mouse XY co-ordinates for use when the tooltip is shown:</p>

<textarea name="code" class="JScript" rows="1" cols="60">
    _overTrigger : function(x, y) {
        this._currTrigger.mouseX = x;
        this._currTrigger.mouseY = y;
    }
</textarea>

<h4>Setting Tooltip Content</h4>

<p>Since the content for a tooltip is usually a function of the trigger node and not constant, <code>Tooltip</code> provides a number of ways to set the content.</p>

<ol>
    <li>Setting the <code>content</code> attribute to a string or node. In this case, the value of the <code>content</code> attribute is used
    for all triggerNodes</li>
    <li>Setting the <code>content</code> attribute to an object literal, containing a map of triggerNode id to content. The content for a trigger node
    will be set using the map, when the tooltip is triggered for the node.</li>
    <li>Setting the title attribute on the trigger node. The value of the title attribute is used to set the tooltip content,
    when triggered for the node.</li>
    <li>By calling the <code>setTriggerContent</code> method to set content for a specific trigger node, in a <code>triggerEnter</code> event listener.</li>
</ol>

<p>The precedence of these methods is handled in the <code>_setTriggerContent</code> method, invoked when the mouse enters a trigger:</p>

<textarea name="code" class="JScript" rows="1" cols="60">
    _setTriggerContent : function(node) {
        var content = this.get("content");
        if (content && !(content instanceof Node || Lang.isString(content))) {
            content = content[node.get("id")] || node.getAttribute("title");
        }
        this.setTriggerContent(content);
    },

    setTriggerContent : function(content) {
        var contentBox = this.get("contentBox");
        contentBox.set("innerHTML", "");

        if (content) {
            if (content instanceof Node) {
                for (var i = 0, l = content.size(); i < l; ++i) {
                    contentBox.appendChild(content.item(i));
                }
            } else if (Lang.isString(content)) {
                contentBox.set("innerHTML", content);
            }
        }
    }
</textarea>

<p>Calling the public <code>setTriggerContent</code> in a <code>triggerEvent</code> listener will over-ride content set using the <code>content</code> attribute or the trigger node's title value.</p>

<h4>Using Tooltip</h4>

<p>For this example, we set up 4 DIV elements which will act as tooltip triggers. They are all marked using a <code>yui-hastooltip</code> class, so that they can be queried using a simple selector, passed as the value for the <code>triggerNodes</code> attribute in the tooltip's constructor Also all 4 trigger nodes are contained in a wrapper DIV with <code>id="delegate"</code> which will act as the <code>delegate</code> node.</p>

<textarea name="code" class="JScript" rows="1" cols="60">
    var tt = new Tooltip({
        triggerNodes:".yui-hastooltip",
        delegate: "#delegate",
        content: {
            tt3: "Tooltip 3 (from lookup)"
        },
        shim:false,
        zIndex:2
    });
    tt.render();
</textarea>

<p>The tooltip content for each of the trigger nodes is setup differently. The first trigger node uses the title attribute to set it's content. The third trigger node's content is set using the content map set in the constructor above. The second trigger node's content is set using a <code>triggerEnter</code> event listener and the <code>setTriggerContent</code> method as shown below:</p>

<textarea name="code" class="JScript" rows="1" cols="60">
    tt.on("triggerEnter", function(e) {
        var node = e.node;
        if (node && node.get("id") == "tt2") {
            this.setTriggerContent("Tooltip 2 (from triggerEvent)");
        }
    });
</textarea>

<p>The fourth trigger node's content is set using it's title attribute, however it also has a <code>triggerEvent</code> listener which prevents the tooltip from being displayed for it, if the checkbox is checked.</p>

<textarea name="code" class="JScript" rows="1" cols="60">
    var prevent = Y.Node.get("#prevent");
    tt.on("triggerEnter", function(e) {
        var node = e.node;
        if (prevent.get("checked")) {
            if (node && node.get("id") == "tt4") {
                e.preventDefault();
            }
        }
    });
</textarea>
				</div>
				<div class="yui-u sidebar">
					
				
					<div id="examples" class="mod box4">
                        <div class="hd">
						<h4>
    Widget Examples:</h4>
                        </div>
						<div class="bd">
							<ul>
								<li><a href='../widget/widget-extend.html'>Extending the base widget class</a></li><li><a href='../widget/widget-build.html'>Creating custom widget classes</a></li><li class='selected'><a href='../widget/widget-tooltip.html'>Creating a simple Tooltip widget</a></li>							</ul>
						</div>
					</div>
					
					<div class="mod box4">
                        <div class="hd">
						<h4>More Widget Resources:</h4>
                        </div>
                        <div class="bd">
						<ul>
							<!-- <li><a href="http://developer.yahoo.com/yui/widget/">User's Guide</a> (external)</li> -->
						<li><a href="../../api/module_widget.html">API Documentation</a></li>
</ul>
                        </div>
					</div>
			  </div>
		</div>
		
		</div>
	</div>


<div class="yui-b toc3" id="tocWrapper">
<!-- TABLE OF CONTENTS -->
<div id="toc">
	
<ul>
<li class="sect first">YUI 3.x Project</li><li class="item"><a title="The Yahoo! User Interface (YUI) Library, 3.x Branch, " href="http://developer.yahoo.com/yui/3/">YUI 3 Web Site (external)</a></li><li class="item"><a title="Examples of every YUI utility and control in action" href="../../examples/">YUI 3 Examples</a></li><li class="item"><a title="Instantly searchable API documentation for the entire YUI library." href="../../api/">YUI 3 API Docs</a></li><li class="item"><a title="The YUI Library can be downloaded from SourceForge" href="http://sourceforge.net/projects/yui/">YUI 3 on Sourceforge (external)</a></li><li class="item"><a title="YUI is free and open, offered under a BSD license." href="http://developer.yahoo.com/yui/3/license.html">YUI License (external)</a></li><li class="sect">YUI 3 Core - Examples</li><li class="item"><a title="YUI (Global Prerequisite) - Functional Examples" href="../../examples/yui/index.html">YUI (Global Prerequisite)</a></li><li class="item"><a title="Event - Functional Examples" href="../../examples/event/index.html">Event</a></li><li class="item"><a title="Node - Functional Examples" href="../../examples/node/index.html">Node</a></li><li class="sect">YUI 3 Component Infrastructure - Examples</li><li class="item"><a title="Attribute - Functional Examples" href="../../examples/attribute/index.html">Attribute</a></li><li class="item"><a title="Plugin - Functional Examples" href="../../examples/plugin/index.html">Plugin</a></li><li class="selected "><a title="Widget - Functional Examples" href="../../examples/widget/index.html">Widget</a></li><li class="sect">YUI 3 Utilities - Examples</li><li class="item"><a title="Animation - Functional Examples" href="../../examples/anim/index.html">Animation</a></li><li class="item"><a title="Cache - Functional Examples" href="../../examples/cache/index.html">Cache</a></li><li class="item"><a title="Cookie - Functional Examples" href="../../examples/cookie/index.html">Cookie</a></li><li class="item"><a title="DataSchema - Functional Examples" href="../../examples/dataschema/index.html">DataSchema</a></li><li class="item"><a title="DataSource - Functional Examples" href="../../examples/datasource/index.html">DataSource</a></li><li class="item"><a title="DataType - Functional Examples" href="../../examples/datatype/index.html">DataType</a></li><li class="item"><a title="Drag &amp; Drop - Functional Examples" href="../../examples/dd/index.html">Drag &amp; Drop</a></li><li class="item"><a title="Get - Functional Examples" href="../../examples/get/index.html">Get</a></li><li class="item"><a title="History - Functional Examples" href="../../examples/history/index.html">History</a></li><li class="item"><a title="ImageLoader - Functional Examples" href="../../examples/imageloader/index.html">ImageLoader</a></li><li class="item"><a title="IO - Functional Examples" href="../../examples/io/index.html">IO</a></li><li class="item"><a title="JSON (JavaScript Object Notation) - Functional Examples" href="../../examples/json/index.html">JSON</a></li><li class="item"><a title="Queue - Functional Examples" href="../../examples/queue/index.html">Queue</a></li><li class="item"><a title="Stylesheet - Functional Examples" href="../../examples/stylesheet/index.html">Stylesheet</a></li><li class="sect">YUI 3 Widgets - Examples</li><li class="item"><a title="Overlay - Functional Examples" href="../../examples/overlay/index.html">Overlay</a></li><li class="item"><a title="Slider - Functional Examples" href="../../examples/slider/index.html">Slider</a></li><li class="sect">YUI 3 Node Plugins - Examples</li><li class="item"><a title="FocusManager Node Plugin - Functional Examples" href="../../examples/node-focusmanager/index.html">FocusManager Node Plugin</a></li><li class="item"><a title="MenuNav Node Plugin - Functional Examples" href="../../examples/node-menunav/index.html">MenuNav Node Plugin</a></li><li class="sect">YUI 3 CSS - Examples</li><li class="item"><a title="YUI CSS Reset - Functional Examples" href="../../examples/cssreset/index.html">CSS Reset</a></li><li class="item"><a title="YUI Fonts - Functional Examples" href="../../examples/cssfonts/index.html">CSS Fonts</a></li><li class="item"><a title="YUI Base - Functional Examples" href="../../examples/cssbase/index.html">CSS Base</a></li><li class="sect">YUI 3 Developer Tools - Examples</li><li class="item"><a title="Console - Functional Examples" href="../../examples/console/index.html">Console</a></li><li class="item"><a title="Profiler - Functional Examples" href="../../examples/profiler/index.html">Profiler</a></li><li class="item"><a title="Test - Functional Examples" href="../../examples/test/index.html">Test</a></li><li class="sect">The YUI Community</li><li class="item"><a title="The Yahoo! User Interface Blog" href="http://yuiblog.com">YUI Blog (external)</a></li><li class="item"><a title="The Yahoo! Group YDN-JavaScript hosts the YUI community forum" href="http://tech.groups.yahoo.com/group/ydn-javascript/">YUI Forum (external)</a></li><li class="item"><a title="The Yahoo! Group yui3 is dedicated to the 3.x branch of the Yahoo! User Interface (YUI) Library." href="http://tech.groups.yahoo.com/group/yui3/">YUI 3 Forum (external)</a></li><li class="item"><a title="YUI is used by Yahoo! and by hundreds of other sites, including many you know and love." href="/yui/poweredby/">YUI Sightings (external)</a></li><li class="item"><a title="Videos and podcasts from the YUI Team and from the Yahoo! frontend engineering community." href="http://developer.yahoo.com/yui/theater/">YUI Theater (external)</a></li><li class="sect">YUI Articles on the YUI Website</li><li class="item"><a title="Answers to Frequently Asked Questions about the YUI Library" href="http://developer.yahoo.com/yui/articles/faq/">YUI FAQ (external)</a></li><li class="item"><a title="Yahoo!'s philosophy of Graded Browser Support" href="http://developer.yahoo.com/yui/articles/gbs/">Graded Browser Support (external)</a></li><li class="item"><a title="Reporting Bugs and Making Feature Requests for YUI Components" href="http://developer.yahoo.com/yui/articles/reportingbugs/">Bug Reports/Feature Requests (external)</a></li><li class="item"><a title="Serve YUI source files from Yahoo! -- free, fast, and simple" href="http://developer.yahoo.com/yui/3/articles/hosting/">Serving YUI Files from Yahoo! (external)</a></li></ul>
</div>
</div>
	</div><!--closes bd-->

	<div id="ft">
        <p class="first">Copyright &copy; 2009 Yahoo! Inc. All rights reserved.</p>
        <p><a href="http://privacy.yahoo.com/privacy/us/devel/index.html">Privacy Policy</a> - 
            <a href="http://docs.yahoo.com/info/terms/">Terms of Service</a> - 
            <a href="http://docs.yahoo.com/info/copyright/copyright.html">Copyright Policy</a> - 
            <a href="http://careers.yahoo.com/">Job Openings</a></p>
	</div>
</div>
<script src="../../assets/dpSyntaxHighlighter.js"></script>
<script language="javascript"> 
dp.SyntaxHighlighter.HighlightAll('code'); 
</script>
</body>
</html>
